#import "CQChatController.h"

#import "CQChatRoomController.h"
#import "CQChatTableCell.h"
#import "CQConnectionsController.h"
#import "CQDirectChatController.h"

#import <ChatCore/MVChatConnection.h>
#import <ChatCore/MVChatRoom.h>
#import <ChatCore/MVChatUser.h>
#import <ChatCore/MVDirectChatConnection.h>

static CQChatController *sharedInstance = nil;

@implementation CQChatController
+ (CQChatController *) defaultController {
	return ( sharedInstance ? sharedInstance : ( sharedInstance = [[self alloc] init] ) );
}

- (id) init {
	if( ! ( self = [super init] ) )
		return nil;

	_chatControllers = [[NSMutableArray alloc] init];

	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_joinedRoom:) name:MVChatRoomJoinedNotification object:nil];
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_gotRoomMessage:) name:MVChatRoomGotMessageNotification object:nil];
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_gotPrivateMessage:) name:MVChatConnectionGotPrivateMessageNotification object:nil];
	[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_gotDirectChatMessage:) name:MVDirectChatConnectionGotMessageNotification object:nil];

	return self;
}

- (void) dealloc {
	[_currentChatViewController release];
	[_chatControllers release];
	[super dealloc];
}

#pragma mark -

/*
- (BOOL) respondsToSelector:(SEL) selector {
	NSLog(@"respondsToSelector: %s", selector);
	return [super respondsToSelector:selector];
}
*/

- (UIWindow *) chatsWindow {
	if( ! _chatsWindow ) {
		CGRect screenRect = [UIHardware fullScreenApplicationContentRect];

		_chatsWindow = [[UIWindow alloc] initWithContentRect:screenRect];

		_navigationBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0., 0., screenRect.size.width, 45.)];
		[_navigationBar setDelegate:self];
		[_navigationBar showLeftButton:nil withStyle:0 rightButton:nil withStyle:0];

		UINavigationItem *item = [[UINavigationItem alloc] initWithTitle:@"Colloquy"];
		[_navigationBar pushNavigationItem:item];
		[item release];

		CGRect contentRect = screenRect;
		contentRect.origin.y = 0.;

		UIView *contentView = [[UIView alloc] initWithFrame:contentRect];

		contentRect.size.height -= 85.;

		_chatsSectionList = [[UISectionList alloc] initWithFrame:contentRect];
		[_chatsSectionList setAllowsScrollIndicators:YES];
		[_chatsSectionList setShouldHideHeaderInShortLists:NO];
		[_chatsSectionList setDataSource:self];

		UISectionTable *table = [_chatsSectionList table];
		[table setDelegate:self];
		[table setAllowsReordering:YES];
		[table setSeparatorStyle:1];
		[table setRowHeight:75.];
		[table setShowScrollerIndicators:YES];

		UITableColumn *column = [[UITableColumn alloc] initWithTitle:@"Chats" identifier:@"chats" width:contentRect.size.width];
		[table addTableColumn:column];

		NSDictionary *buttonItem = [NSDictionary dictionaryWithObjectsAndKeys:[CQConnectionsController defaultController], kUIToolbarButtonTarget, @"showConnections", kUIToolbarButtonAction, @"connections.png", kUIToolbarButtonInfo, [NSNumber numberWithUnsignedInt:1], kUIToolbarButtonTag, [NSValue valueWithSize:NSMakeSize(0., 2.)], kUIToolbarButtonInfoOffset, nil];
		NSArray *items = [NSArray arrayWithObjects:buttonItem, nil];
		_buttonBar = [[UIToolbar alloc] initInView:contentView withFrame:CGRectMake(0., screenRect.size.height - 40., screenRect.size.width, 40.) withItemList:items];

		int buttons[1] = { 1 };
		[_buttonBar registerButtonGroup:1 withButtons:buttons withCount:1];
		[_buttonBar showButtonGroup:1 withDuration:0.];

		contentRect.origin.y = 45.;
		contentRect.size.height += 45.;

		_mainView = [[UIView alloc] initWithFrame:contentRect];
		[_mainView addSubview:_chatsSectionList]; 

		[contentView addSubview:_navigationBar]; 
		[contentView addSubview:_mainView]; 
		[contentView addSubview:_buttonBar]; 

		[_chatsWindow setContentView:contentView];
	}

	return _chatsWindow;
}

- (UINavigationBar *) navigationBar {
	[self chatsWindow];
	return _navigationBar;
}

- (UIToolbar *) buttonBar {
	[self chatsWindow];
	return _buttonBar;
}

- (void) showChatsWindow {
	[self chatsWindow];

	[_chatsSectionList reloadData];

	[_chatsWindow orderFront:nil];
	[_chatsWindow makeKey:nil];
}

#pragma mark -

- (void) updateApplicationBadge {
	if( _badgeNumber > 0 && [UIApp isSuspended] )
		[UIApp setApplicationBadge:[NSString stringWithFormat:@"%u", _badgeNumber]];
	else [UIApp removeApplicationBadge];
}

- (void) clearApplicationBadge {
	[UIApp removeApplicationBadge];
	_badgeNumber = 0;
}

- (void) incrementApplicationBadgeByAmount:(unsigned) amount {
	if( ! [UIApp isSuspended] || ! amount ) return;
	_badgeNumber += amount;
	[self updateApplicationBadge];
}

- (void) decrementApplicationBadgeByAmount:(unsigned) amount {
	if( ! [UIApp isSuspended] || ! amount ) return;
	_badgeNumber -= MIN( _badgeNumber, amount );
	[self updateApplicationBadge];
}

#pragma mark -

- (int) numberOfSectionsInSectionList:(UISectionList *) sectionList {
	NSMutableSet *connections = [NSMutableSet set];

	CQDirectChatController *controller = nil;
	NSEnumerator *enumerator = [_chatControllers objectEnumerator];
	while( ( controller = [enumerator nextObject] ) )
		if( [controller connection] )
			[connections addObject:[controller connection]];

	return [connections count];
}

- (NSString *) sectionList:(UISectionList *) sectionList titleForSection:(int) section {
	NSMutableSet *connections = [NSMutableSet set];

	CQDirectChatController *controller = nil;
	NSEnumerator *enumerator = [_chatControllers objectEnumerator];
	while( ( controller = [enumerator nextObject] ) ) {
		if( [controller connection] ) {
			[connections addObject:[controller connection]];
			if( ( section + 1 ) == [connections count] )
				return [[controller connection] server];
		}
	}

	return @"Other";
}

- (int) sectionList:(UISectionList *) sectionList rowForSection:(int) section {
	NSMutableSet *connections = [NSMutableSet set];

	unsigned row = 0;
	CQDirectChatController *controller = nil;
	NSEnumerator *enumerator = [_chatControllers objectEnumerator];
	while( ( controller = [enumerator nextObject] ) ) {
		if( [controller connection] ) {
			[connections addObject:[controller connection]];
			if( ( section + 1 ) == [connections count] )
				return row;
			++row;
		}
	}

	return row;
}

#pragma mark -

- (int) numberOfRowsInTable:(UITableView *) table {
	return [_chatControllers count];
}

- (UITableViewCell *) table:(UITableView *) table cellForRow:(int) row column:(int) col {
	id item = [_chatControllers objectAtIndex:row];
	CQChatTableCell *cell = [item tableCell];
	[cell setEnableReordering:YES animated:YES];
	return (UITableViewCell *)cell;
}

- (BOOL) table:(UITableView *) table canDeleteRow:(int) row {
	return YES;
}

- (BOOL) table:(UITableView *) table canMoveRow:(int) row {
	return YES;
}

- (BOOL) table:(UITableView *) table shouldIndentRow:(int) row {
	return NO;
}

- (BOOL) table:(UITableView *) table showDisclosureForRow:(int) row {
	return NO;
}

- (void) table:(UITableView *) table willSwipeToDeleteRow:(int) row {
	
}

- (void) table:(UITableView *) table deleteRow:(int) row {
	id controller = [_chatControllers objectAtIndex:row];
	if( ! controller ) return;

	[self closeViewController:controller];
}

- (void) table:(UITableView *) table movedRow:(int) oldRow toRow:(int) newRow {
	
}

- (int) table:(UITableView *) table moveDestinationForRow:(int)row withSuggestedDestinationRow:(int) suggestedRow {
	return suggestedRow;
}

- (BOOL) table:(UITableView *) table canSelectRow:(int) row {
	return ( ! _showingChat && ! _editingList );
}

- (void) tableRowSelected:(NSNotification *) notification {
	if( ! _showingChat && ! _editingList ) {
		int selectedRow = [[_chatsSectionList table] selectedRow];
		CQDirectChatController *controller = [_chatControllers objectAtIndex:selectedRow];

		[_currentChatViewController release];
		_currentChatViewController = [controller retain];

		UINavigationItem *navigationItem = [[UINavigationItem alloc] initWithTitle:[controller displayName]];
		[_navigationBar pushNavigationItem:navigationItem];
		[navigationItem release];

		[_navigationBar showLeftButton:nil withStyle:0 rightButton:nil withStyle:0];
		
		UIView *controllerView = [controller view];

		CGRect controllerViewFrame = [controllerView frame];
		controllerViewFrame.origin.x = [_mainView frame].size.width;

		[controllerView setFrame:controllerViewFrame];

		[[[_mainView _window] contentView] addSubview:controllerView];

		[controller willShow];

		NSMutableArray *animations = [[NSMutableArray alloc] initWithCapacity:3];
	
		CGRect endFrame = [_buttonBar frame];
		endFrame.origin.x = -endFrame.size.width;

		UIFrameAnimation *buttonBarAnimation = [[UIFrameAnimation alloc] initWithTarget:_buttonBar];
		[buttonBarAnimation setStartFrame:[_buttonBar frame]];
		[buttonBarAnimation setEndFrame:endFrame];
		[buttonBarAnimation setSignificantRectFields:1]; // the x position of the rect

		[animations addObject:buttonBarAnimation];
		[buttonBarAnimation release];

		endFrame = [_mainView frame];
		endFrame.origin.x = -endFrame.size.width;

		UIFrameAnimation *mainViewAnimation = [[UIFrameAnimation alloc] initWithTarget:_mainView];
		[mainViewAnimation setStartFrame:[_mainView frame]];
		[mainViewAnimation setEndFrame:endFrame];
		[mainViewAnimation setSignificantRectFields:1]; // the x position of the rect

		[animations addObject:mainViewAnimation];
		[mainViewAnimation release];

		endFrame = controllerViewFrame;
		endFrame.origin.x = 0.;

		UIFrameAnimation *chatViewAnimation = [[UIFrameAnimation alloc] initWithTarget:controllerView];
		[chatViewAnimation setStartFrame:controllerViewFrame];
		[chatViewAnimation setEndFrame:endFrame];
		[chatViewAnimation setSignificantRectFields:1]; // the x position of the rect
		[chatViewAnimation setDelegate:self];

		[animations addObject:chatViewAnimation];
		[chatViewAnimation release];

		[[UIAnimator sharedAnimator] addAnimations:animations withDuration:0.33 start:YES];

		[animations release];
	}
}

#pragma mark -

- (void) animator:(UIAnimator *) animator stopAnimation:(UIAnimation *) animation {
	if ( [animation target] == _mainView ) {
		_showingChat = NO;
		[[_currentChatViewController view] removeFromSuperview];
		[_currentChatViewController didHide];
		[_currentChatViewController release];
		_currentChatViewController = nil;
	} else {
		_showingChat = YES;
		[_currentChatViewController didShow];
	}
}

#pragma mark -

- (void) navigationBar:(UINavigationBar *) bar poppedItem:(UINavigationItem *) item {
	[_currentChatViewController willHide];

	[[_chatsSectionList table] selectRow:-1 byExtendingSelection:NO withFade:NO scrollingToVisible:NO];

	[_navigationBar showLeftButton:nil withStyle:0 rightButton:nil withStyle:0];

	NSMutableArray *animations = [[NSMutableArray alloc] initWithCapacity:3];
	
	CGRect endFrame = [_buttonBar frame];
	endFrame.origin.x = 0.;

	UIFrameAnimation *buttonBarAnimation = [[UIFrameAnimation alloc] initWithTarget:_buttonBar];
	[buttonBarAnimation setStartFrame:[_buttonBar frame]];
	[buttonBarAnimation setEndFrame:endFrame];
	[buttonBarAnimation setSignificantRectFields:1]; // the x position of the rect

	[animations addObject:buttonBarAnimation];
	[buttonBarAnimation release];

	endFrame = [_mainView frame];
	endFrame.origin.x = 0.;

	UIFrameAnimation *mainViewAnimation = [[UIFrameAnimation alloc] initWithTarget:_mainView];
	[mainViewAnimation setStartFrame:[_mainView frame]];
	[mainViewAnimation setEndFrame:endFrame];
	[mainViewAnimation setSignificantRectFields:1]; // the x position of the rect
	[mainViewAnimation setDelegate:self];

	[animations addObject:mainViewAnimation];
	[mainViewAnimation release];

	endFrame = [[_currentChatViewController view] frame];
	endFrame.origin.x = [_mainView frame].size.width;

	UIFrameAnimation *chatViewAnimation = [[UIFrameAnimation alloc] initWithTarget:[_currentChatViewController view]];
	[chatViewAnimation setStartFrame:[[_currentChatViewController view] frame]];
	[chatViewAnimation setEndFrame:endFrame];
	[chatViewAnimation setSignificantRectFields:1]; // the x position of the rect

	[animations addObject:chatViewAnimation];
	[chatViewAnimation release];

	[[UIAnimator sharedAnimator] addAnimations:animations withDuration:0.33 start:YES];

	[animations release];
}

- (void) navigationBar:(UINavigationBar *) bar buttonClicked:(int) button {
	if( _showingChat ) {
		if( [_currentChatViewController respondsToSelector:@selector(navigationBar:buttonClicked:)] )
			[_currentChatViewController navigationBar:bar buttonClicked:button];
	} else {
		if( button == 0 ) {
			if( _editingList ) {
				[_navigationBar showLeftButton:nil withStyle:0 rightButton:@"Edit" withStyle:0];
				_editingList = NO;
			} else {
				[_navigationBar showLeftButton:nil withStyle:0 rightButton:@"Done" withStyle:3];
				_editingList = YES;
			}

			for( unsigned row = 0; row < [self numberOfRowsInTable:[_chatsSectionList table]]; ++row) {
				UITableViewCell *cell = [[_chatsSectionList table] cellAtRow:row column:0];
				[[_chatsSectionList table] _enableRowDeletion:YES forCell:cell atRow:row allowInsert:NO allowReorder:_editingList animated:YES];
			}
		}
	}
}

#pragma mark -

static NSComparisonResult sortByConnectionAscending(CQDirectChatController *chatController1, CQDirectChatController *chatController2, void *context) {
	return [[[chatController1 connection] server] caseInsensitiveCompare:[[chatController2 connection] server]];
}

- (void) _sortChatControllers {
	[_chatControllers sortUsingFunction:sortByConnectionAscending context:NULL];
	[_chatsSectionList reloadData];
}

- (void) _joinedRoom:(NSNotification *) notification {
	MVChatRoom *room = [notification object];
	if( ! [[CQConnectionsController defaultController] managesConnection:[room connection]] )
		return;
	CQChatRoomController *roomController = [self chatViewControllerForRoom:room ifExists:NO];
	[roomController joined];
}

- (void) _gotRoomMessage:(NSNotification *) notification {
	// we do this here to make sure we catch early messages right when we join (this includes dircproxy's dump)
	MVChatRoom *room = [notification object];
	CQChatRoomController *controller = [self chatViewControllerForRoom:room ifExists:NO];

	MVChatUser *user = [[notification userInfo] objectForKey:@"user"];
	NSData *message = [[notification userInfo] objectForKey:@"message"];
	CQChatMessageType type = ( [[[notification userInfo] objectForKey:@"notice"] boolValue] ? CQChatMessageNoticeType : CQChatMessageNormalType );
	[controller addMessageToDisplay:message fromUser:user withAttributes:[notification userInfo] withIdentifier:[[notification userInfo] objectForKey:@"identifier"] andType:type];
}

- (void) _gotPrivateMessage:(NSNotification *) notification {
	MVChatUser *user = [notification object];
	if( ! [[CQConnectionsController defaultController] managesConnection:[user connection]] )
		return;

	BOOL hideFromUser = NO;

	if( [[[notification userInfo] objectForKey:@"notice"] boolValue] ) {
		if( ! [self chatViewControllerForUser:user ifExists:YES] )
			hideFromUser = YES;

		if( [[NSUserDefaults standardUserDefaults] boolForKey:@"CQChatAlwaysShowNotices"] )
			hideFromUser = NO;
	}

	if( ! hideFromUser ) {
		CQDirectChatController *controller = [self chatViewControllerForUser:user ifExists:NO userInitiated:NO];
		NSData *message = [[notification userInfo] objectForKey:@"message"];
		CQChatMessageType type = ( [[[notification userInfo] objectForKey:@"notice"] boolValue] ? CQChatMessageNoticeType : CQChatMessageNormalType );
		[controller addMessageToDisplay:message fromUser:user withAttributes:[notification userInfo] withIdentifier:[[notification userInfo] objectForKey:@"identifier"] andType:type];
	}
}

- (void) _gotDirectChatMessage:(NSNotification *) notification {
	MVDirectChatConnection *connection = [notification object];
	MVChatUser *user = [connection user];
	if( ! [[CQConnectionsController defaultController] managesConnection:[user connection]] )
		return;

	NSData *message = [[notification userInfo] objectForKey:@"message"];

	CQDirectChatController *controller = [self chatViewControllerForDirectChatConnection:connection ifExists:NO];
	[controller addMessageToDisplay:message fromUser:user withAttributes:[notification userInfo] withIdentifier:[[notification userInfo] objectForKey:@"identifier"] andType:CQChatMessageNormalType];
}

#pragma mark -

- (NSArray *) allChatViewControllers {
	return _chatControllers;
}

- (NSArray *) chatViewControllersWithConnection:(MVChatConnection *) connection {
	NSParameterAssert( connection != nil );

	NSMutableArray *result = [NSMutableArray array];
	NSEnumerator *enumerator = [_chatControllers objectEnumerator];
	CQDirectChatController *controller = nil;

	while( ( controller = [enumerator nextObject] ) )
		if( [controller connection] == connection )
			[result addObject:controller];

	return result;
}

- (NSArray *) chatViewControllersOfClass:(Class) class {
	NSParameterAssert( class != NULL );

	NSMutableArray *result = [NSMutableArray array];
	NSEnumerator *enumerator = [_chatControllers objectEnumerator];
	id controller = nil;

	while( ( controller = [enumerator nextObject] ) )
		if( [controller isMemberOfClass:class] )
			[result addObject:controller];

	return result;
}

- (NSArray *) chatViewControllersKindOfClass:(Class) class {
	NSParameterAssert( class != NULL );

	NSMutableArray *result = [NSMutableArray array];
	NSEnumerator *enumerator = [_chatControllers objectEnumerator];
	id controller = nil;

	while( ( controller = [enumerator nextObject] ) )
		if( [controller isKindOfClass:class] )
			[result addObject:controller];

	return result;
}

#pragma mark -

- (CQChatRoomController *) chatViewControllerForRoom:(MVChatRoom *) room ifExists:(BOOL) exists {
	NSParameterAssert( room != nil );

	NSEnumerator *enumerator = [_chatControllers objectEnumerator];
	CQChatRoomController *controller = nil;

	while( ( controller = [enumerator nextObject] ) )
		if( [controller isMemberOfClass:[CQChatRoomController class]] && [[controller target] isEqual:room] )
			return controller;

	if( ! exists ) {
		if( ( controller = [[CQChatRoomController alloc] initWithTarget:room] ) ) {
			[_chatControllers addObject:controller];
			[controller release];

			[self _sortChatControllers];
			return controller;
		}
	}

	return nil;
}

- (CQDirectChatController *) chatViewControllerForUser:(MVChatUser *) user ifExists:(BOOL) exists {
	return [self chatViewControllerForUser:user ifExists:exists userInitiated:YES];
}

- (CQDirectChatController *) chatViewControllerForUser:(MVChatUser *) user ifExists:(BOOL) exists userInitiated:(BOOL) initiated {
	NSParameterAssert( user != nil );

	NSEnumerator *enumerator = [_chatControllers objectEnumerator];
	CQDirectChatController *controller = nil;

	while( ( controller = [enumerator nextObject] ) )
		if( [controller isMemberOfClass:[CQDirectChatController class]] && [[controller target] isEqual:user] )
			return controller;

	if( ! exists ) {
		if( ( controller = [[CQDirectChatController alloc] initWithTarget:user] ) ) {
			[_chatControllers addObject:controller];
			[controller release];

			[self _sortChatControllers];
			return controller;
		}
	}

	return nil;
}

- (CQDirectChatController *) chatViewControllerForDirectChatConnection:(MVDirectChatConnection *) connection ifExists:(BOOL) exists {
	NSParameterAssert( connection != nil );

	NSEnumerator *enumerator = [_chatControllers objectEnumerator];
	CQDirectChatController *controller = nil;

	while( ( controller = [enumerator nextObject] ) )
		if( [controller isMemberOfClass:[CQDirectChatController class]] && [[controller target] isEqual:connection] )
			break;

	if( ! exists ) {
		if( ( controller = [[CQDirectChatController alloc] initWithTarget:connection] ) ) {
			[_chatControllers addObject:controller];
			[controller release];

			[self _sortChatControllers];
			return controller;
		}
	}

	return nil;
}

#pragma mark -

- (void) closeViewController:(id) controller {
	if( [controller respondsToSelector:@selector( willClose )] )
		[controller willClose];
	[_chatControllers removeObjectIdenticalTo:controller];
	[_chatsSectionList reloadData];
}
@end
